﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using System.ComponentModel;

namespace Asol.Reporting.Support.Components
{
    #region class DataEditForm
    /// <summary>
    /// Formulář pro editaci datového objektu
    /// </summary>
    public class DataEditForm : DForm
    {
        #region public static metoda EditObject() pro editaci datového objektu (v jednom řádku kódu)
        /// <summary>
        /// Metoda, která zajistí editaci datového objektu pomocí formuláře DataEditForm, 
        /// a v něm umístěného Controlu, který si předaný datový objekt sám vytvoří.
        /// Metoda vrací true, pokud mají být data z objektu uložena.
        /// </summary>
        /// <param name="data">Editovaná data</param>
        /// <returns></returns>
        public static bool EditObject(IEditableObject data)
        {
            return _EditObject(data, (Rectangle?)null, null);
        }
        /// <summary>
        /// Metoda, která zajistí editaci datového objektu pomocí formuláře DataEditForm, 
        /// a v něm umístěného Controlu, který si předaný datový objekt sám vytvoří.
        /// Metoda vrací true, pokud mají být data z objektu uložena.
        /// </summary>
        /// <param name="data">Editovaná data</param>
        /// <param name="formTitle">Titulek formuláře</param>
        /// <returns></returns>
        public static bool EditObject(IEditableObject data, string formTitle)
        {
            return _EditObject(data, (Rectangle?)null, formTitle);
        }
        /// <summary>
        /// Metoda, která zajistí editaci datového objektu pomocí formuláře DataEditForm, 
        /// a v něm umístěného Controlu, který si předaný datový objekt sám vytvoří.
        /// Metoda vrací true, pokud mají být data z objektu uložena.
        /// </summary>
        /// <param name="data">Editovaná data</param>
        /// <param name="callerBounds">Souřadnice controlu, vedle něhož máme formulář zobrazit</param>
        /// <returns></returns>
        public static bool EditObject(IEditableObject data, Rectangle callerBounds)
        {
            return _EditObject(data, callerBounds, null);
        }
        /// <summary>
        /// Metoda, která zajistí editaci datového objektu pomocí formuláře DataEditForm, 
        /// a v něm umístěného Controlu, který si předaný datový objekt sám vytvoří.
        /// Metoda vrací true, pokud mají být data z objektu uložena.
        /// </summary>
        /// <param name="data">Editovaná data</param>
        /// <param name="callerBounds">Souřadnice controlu, vedle něhož máme formulář zobrazit</param>
        /// <param name="formTitle">Titulek formuláře</param>
        /// <returns></returns>
        public static bool EditObject(IEditableObject data, Rectangle? callerBounds, string formTitle)
        {
            return _EditObject(data, callerBounds, formTitle);
        }
        /// <summary>
        /// Metoda, která zajistí editaci datového objektu pomocí formuláře DataEditForm, 
        /// a v něm umístěného Controlu, který si předaný datový objekt sám vytvoří.
        /// Metoda vrací true, pokud mají být data z objektu uložena.
        /// </summary>
        /// <param name="data">Editovaná data</param>
        /// <param name="callerBounds">Souřadnice controlu, vedle něhož máme formulář zobrazit</param>
        /// <param name="formTitle">Titulek formuláře</param>
        /// <returns></returns>
        private static bool _EditObject(IEditableObject data, Rectangle? callerBounds, string formTitle)
        {
            bool save = false;
            using (DataEditForm form = new DataEditForm())
            {
                form.DataEditableObject = data;
                form.Text = (formTitle != null ? formTitle : Assistant.GetMessage("Zadejte údaje"));
                form.FormConfigPersistent = ConfigPersistentMode.None;
                form.SetLocationNear(callerBounds);
                form.ShowDialog();
                save = form.DataAccepted;
            }
            return save;
        }
        #endregion
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        public DataEditForm()
        {
            InitializeComponent();
        }
        protected void InitializeComponent()
        {
            this.PanelOk = new PanelOk();

            this.SuspendLayout();
         //   this.PanelOk.Bounds = new System.Drawing.Rectangle(0, 100, 500, 45);
            this.PanelOk.Name = "PanelOk";
            this.PanelOk.TabIndex = 1;
            this.PanelOk.Buttons = PanelOkButtons.ApplySaveCancel;
            this.PanelOk.ButtonsAlignment = System.Drawing.ContentAlignment.BottomLeft;
            this.PanelOk.DirtySensitive = true;
            this.PanelOk.Save += new EventHandler(PanelOk_Save);
            this.Controls.Add(this.PanelOk);

            this.SizeChanged += new EventHandler(DataEditForm_SizeChanged);
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        /// <summary>
        /// Panel s tlačítky Použít-Uložit-Zrušit
        /// </summary>
        protected PanelOk PanelOk;
        void PanelOk_Save(object sender, EventArgs e)
        {
            this.DataAccepted = true;
            IEditableControl iec = this._DataEditableControl as IEditableControl;
            if (iec != null)
                iec.GatherData(this._DataEditableObject);
            this.PanelOk.IsDirty = false;
        }
        /// <summary>
        /// Data ve formu mají být uložena
        /// </summary>
        public bool DataAccepted { get; protected set; }
        #endregion
        #region Resize
        void DataEditForm_SizeChanged(object sender, EventArgs e)
        {
            this._FormSizeChanged();
        }
        /// <summary>
        /// Po změně velikosti formuláře
        /// </summary>
        private void _FormSizeChanged()
        {
            if (this._FormSizeChanging) return;
            try
            {
                this._FormSizeChanging = true;
                if (this._DataEditableControl != null)
                {
                    Size size = this.ClientSize;
                    this._DataEditableControl.Bounds = new Rectangle(12, 12, size.Width - 24, this.PanelOk.Top - 18);
                }
            }
            finally
            {
                this._FormSizeChanging = false;
            }
        }
        private void _EditableControlSizeAccept(Control control)
        {
            if (control == null) return;
            Size clientSize = this.ClientSize;
            Size controlSize = control.Size;
            Size innerSize = new Size(controlSize.Width + 24, controlSize.Height + 18 + (clientSize.Height - this.PanelOk.Top));
            int ax = this.Width - clientSize.Width;
            int ay = this.Height - clientSize.Height;

            if (this._FormSizeChanging) return;
            try
            {
                this._FormSizeChanging = true;
                this.Size = new Size(innerSize.Width + ax, innerSize.Height + ay);
            }
            finally
            {
                this._FormSizeChanging = false;
            }
        }
        private bool _FormSizeChanging;
        #endregion
        #region DataEditableObject : datový objekt, který se v tomto formuláři edituje
        /// <summary>
        /// DataEditableObject : datový objekt, který se v tomto formuláři edituje.
        /// Vložením objektu do této property se aktivuje jeho metoda IEditableObject.CreateControl(), 
        /// která vytvoří typicky Panel, tento panel je umístěn do this formuláře, a jsou podle něj upraveny rozměry (Size) tohoto formuláře.
        /// </summary>
        [Browsable(false)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEditableObject DataEditableObject
        {
            get { return this._DataEditableObject; }
            set
            {
                if (this._DataEditableControl != null)
                {
                    this.Controls.Remove(this._DataEditableControl);
                    if (!this._DataEditableControl.IsDisposed)
                        this._DataEditableControl.Dispose();
                    this._DataEditableControl = null;
                }

                this._DataEditableObject = value;
                if (this._DataEditableObject != null)
                    this.InitializeDataEditableControl(value.CreateControl());
            }
        }
        private void InitializeDataEditableControl(Control control)
        {
            if (control == null) return;
            this._DataEditableControl = control;
            control.Name = "DataControl";
            control.Location = new System.Drawing.Point(12, 12);
            control.TabIndex = 0;
            control.Anchor = AnchorStyles.None;

            IEditableControl iec = control as IEditableControl;
            if (iec != null)
                iec.ContentChanged +=new EventHandler(_DataControlContentChanged);

            this._EditableControlSizeAccept(control);
            this.Controls.Add(control);
        }

        void _DataControlContentChanged(object sender, EventArgs e)
        {
            this.PanelOk.IsDirty = true;
        }
        private IEditableObject _DataEditableObject;
        private Control _DataEditableControl;
        #endregion
    }
    #endregion
    #region interface IEditableObject, IEditableControl
    /// <summary>
    /// IEditableObject předepisuje vlastnosti pro objekt, který může být snadno editován v okně DataEditForm.
    /// Základní vlastností tohoto datového objektu je metoda CreateControl(), která vygeneruje jakýkoli Control, který zobrazuje jeho data.
    /// Vygenerovaný control musí být WinForm control.
    /// Měl by implementovat interface IEditableControl, jehož metoda AcceptData() je volána v okamžiku stisknutí tlačítka "Uložit", viz tam.
    /// </summary>
    public interface IEditableObject
    {
        /// <summary>
        /// Vytvoří a vrátí WinForm Panel, v němž bude zobrazen obsah objektu.
        /// </summary>
        /// <returns></returns>
        Control CreateControl();
    }
    /// <summary>
    /// IEditableControl předepisuje vlastnosti pro vizuální control, 
    /// díky nimž tento Control dostane informaci o uložení editovaných dat z vizuální vrstvy do datového objektu.
    /// </summary>
    public interface IEditableControl
    {
        /// <summary>
        /// Událost, kdy na controlu došlo ke změně dat, a má se nastavit stav Dirty
        /// </summary>
        event EventHandler ContentChanged;
        /// <summary>
        /// Metoda, kterou volá editační formulář při akceptování zadaných dat.
        /// Editační control by měl v této metodě sesbírat data z vizuální vrstvy auložit do datového objektu.
        /// </summary>
        void GatherData(IEditableObject data);
    }
    #endregion
    #region class EditableControl : bázová třída pro control, který dovoluje editovat data třídy.
    /// <summary>
    /// Vizuální control pro editaci dat jednoho serveru
    /// </summary>
    public abstract class EditableControl<D> : DPanel, IEditableControl where D : class, IEditableObject
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="data"></param>
        public EditableControl(D data)
        {
            this.InitializeControls();
            this.Data = data;
        }
        /// <summary>
        /// Datový objekt, který je tímto controlem zobrazován
        /// </summary>
        public D Data
        {
            get { return this._Data; }
            set
            {
                this._Data = value;
                this.ShowData(value);
            }
        } private D _Data;
        /// <summary>
        /// Metoda, kterou je možno zavolat z jakéhokoli controlu (TextBox, atd) po změně hodnoty
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        protected void AnyControlChanged(object sender, EventArgs args)
        {
            this.OnContentChanged();
        }
        /// <summary>
        /// Vyvolá událost ContentChanged
        /// </summary>
        protected virtual void OnContentChanged()
        {
            if (this.ContentChanged != null)
                this.ContentChanged(this, EventArgs.Empty);
        }
        private event EventHandler ContentChanged;
        #endregion
        #region abstract members: init, show and gather data
        /// <summary>
        /// Vytvoří všechny controly a nastaví velikost controlu.
        /// Neplní data.
        /// </summary>
        protected abstract void InitializeControls();
        /// <summary>
        /// Zobrazí data z datového objektu ve vizuálních controlech
        /// </summary>
        /// <param name="data"></param>
        protected abstract void ShowData(D data);
        /// <summary>
        /// Sesbírá data z vizuálních controlů do datového objektu
        /// </summary>
        /// <param name="data"></param>
        protected abstract void GatherData(D data);
        #endregion
        #region implementace IEditableControl
        /// <summary>
        /// Událost, kdy na controlu došlo ke změně dat, a má se nastavit stav Dirty
        /// </summary>
        event EventHandler IEditableControl.ContentChanged
        {
            add { this.ContentChanged += value; }
            remove { this.ContentChanged -= value; }
        }
        /// <summary>
        /// Metoda, kterou volá editační formulář při akceptování zadaných dat.
        /// Editační control by měl v této metodě sesbírat data z vizuální vrstvy auložit do datového objektu.
        /// </summary>
        void IEditableControl.GatherData(IEditableObject data)
        {
            this.GatherData(data as D);
        }
        #endregion
    }
    #endregion
}
